package Client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import Client.excepciones.ActConnectException;
import Client.excepciones.ActLoginException;
import Client.excepciones.ActTransferException;
import Client.excepciones.IncorrectCodeException;
import Client.excepciones.NullParamIDSensorException;
import Client.excepciones.NullPasswordException;
import Client.excepciones.NullUserException;
import Client.excepciones.SensorNotExistsException;
import Client.excepciones.SensorOFFException;
import Client.excepciones.SensorONException;
import Client.excepciones.UnableToConnectException;
import Client.excepciones.UnexpectedErrorException;
import Client.excepciones.UnknownSensorException;
import Client.excepciones.UserNotInsertedException;
import Client.excepciones.UserNotLoggedException;

import util.SocketManager;

/**
 * Clase para gestionar en el cliente todo lo relacionado con el envio y recepcion de mensajes al servidor.
 * @author inlacou
 *
 */
public class ClienteTCP {

	private SocketManager sm;

	/**
	 * Constructor de la clase, que recibe como parametro la ip del servidor.
	 * @param ip
	 * @throws IOException
	 */
	public ClienteTCP(String ip) throws IOException {
		sm = new SocketManager(ip, 8891);
	}
	
	/**
	 * Funcion para leer la respuesta del servidor al intento de conexion.
	 * @throws IOException
	 * @throws IncorrectCodeException
	 * @throws UnableToConnectException
	 */
	public void leerRespuestaConexion() throws IOException, IncorrectCodeException, UnableToConnectException{
		String s = sm.leer();
		StringTokenizer st = new StringTokenizer(s," ");
		String code = st.nextToken();
		if(code.equals("100")){
			System.out.println("100");
		}else if(code.equals("000")){
			System.out.println("000");
			throw new UnableToConnectException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion que envia al servidor el nombre de usuario.
	 * @param name
	 * @throws IOException
	 */
	public void sendUserName(String name) throws IOException{
		sm.escribir("USER " + name);
	}

	/**
	 * Funcion que recibe la respuesta al nombre de usuario enviado al servidor.
	 * @return true si el nombre de usuario es correcto, false si no lo es.
	 * @throws IOException
	 * @throws IncorrectCodeException 
	 * @throws NullUserException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean waitUserNameAck() throws IOException, IncorrectCodeException, NullUserException, UnexpectedErrorException{
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("210")){
			return true;
		}else if(s.equals("411")){
			return false;
		}else if(s.equals("602")){
			throw new NullUserException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}
		throw new IncorrectCodeException();
	}

	/**
	 * Funcion que envia la contraseña al servidor.
	 * @param pass
	 * @throws IOException
	 */
	public void sendPassword(String pass) throws IOException{
		sm.escribir("PASS " + pass);
	}

	/**
	 * Funcion que recibe del servidor la respuesta a la contraseña enviada.
	 * @return true si la contraseña es correcta, false si no lo es.
	 * @throws IOException
	 * @throws NullPasswordException
	 * @throws IncorrectCodeException 
	 * @throws UserNotInsertedException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean waitPasswordAck() throws IOException, NullPasswordException, IncorrectCodeException, UserNotInsertedException, UnexpectedErrorException {
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("211")){
			return true;
		}else if(s.equals("412")){
			return false;
		}else if(s.equals("413")){
			throw new NullPasswordException();
		}else if(s.equals("600")){
			throw new UserNotInsertedException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}
		throw new IncorrectCodeException();
	}

	/**
	 * Funcion que pide al servidor la lista de los sensores y su estado.
	 * @return ArrayList de Strings con formato [nombre_sensor][ON/OF]
	 * @throws IncorrectCodeException
	 * @throws IOException
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public ArrayList<String[]> listaSensores() throws IncorrectCodeException, IOException, UserNotLoggedException, UnexpectedErrorException {
		sm.escribir("LISTSENSOR");
		String sLinea = sm.leer().trim();
		int linea = 0;
		String[] s;
		ArrayList<String[]> resultado = new ArrayList<String[]>();
		StringTokenizer st = new StringTokenizer(sLinea, " ");
		String code = st.nextToken();
		if(code.equals("122")){
			sLinea = sm.leer();
			while(!sLinea.substring(0, 3).equals("222")){
				st = new StringTokenizer(sLinea, ";");
				s = new String[3];
				for(int param = 0; param < 3; param++){
					s[param] = st.nextToken();
				}
				resultado.add(s);
				sLinea = sm.leer();
				linea++;
			}
		}else if(code.equals("601")){
			throw new UserNotLoggedException();
		}else if(code.equals("666")){
			throw new UnexpectedErrorException();
		} else {
			throw new IncorrectCodeException();
		}
		return resultado;
	}

	/**
	 * Funcion que pide al servidor la lista de medidas del sensor con id igual al introducido por parametro.
	 * @param idSensor
	 * @return ArrayList con formato [fecha][hora][localizacion][valor]
	 * @throws IncorrectCodeException
	 * @throws IOException
	 * @throws UserNotLoggedException
	 * @throws UnexpectedErrorException
	 */
	public ArrayList<String[]> historico(int idSensor) throws IncorrectCodeException, IOException, UserNotLoggedException, UnexpectedErrorException {
		sm.escribir("HISTORICO " + idSensor);
		String sLinea = sm.leer().trim();
		int linea = 0;
		String[] s;
		ArrayList<String[]> resultado = new ArrayList<String[]>();
		StringTokenizer st = new StringTokenizer(sLinea, " ");
		String code = st.nextToken();
		if(code.equals("123")){
			sLinea = sm.leer();
			while(!sLinea.substring(0, 3).equals("222")){
				System.out.println(sLinea);
				st = new StringTokenizer(sLinea, ";");
				s = new String[4];
				for(int param = 0; param < 4; param++){
					s[param] = st.nextToken();
				}
				resultado.add(s);
				sLinea = sm.leer();
				linea++;
			}
		}else if(code.equals("601")){
			throw new UserNotLoggedException();
		}else if(code.equals("666")){
			throw new UnexpectedErrorException();
		} else {
			throw new IncorrectCodeException();
		}
		return resultado;
	}

	/**
	 * Funcion que envia al servidor la orden de encender el sensor de numero pasado por parametro.
	 * @param idSensor
	 * @return true si enciende el sensor, false si ya estaba encendido.
	 * @throws IOException
	 * @throws SensorNotExistsException
	 * @throws IncorrectCodeException 
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean ON(int idSensor) throws IOException, SensorNotExistsException, SensorONException, IncorrectCodeException, UserNotLoggedException, UnexpectedErrorException{
		sm.escribir("ON " + idSensor);
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("213")){
			return true;
		}else if(s.equals("427")){
			throw new SensorNotExistsException();
		}else if(s.equals("428")){
			throw new SensorONException();
		}else if(s.equals("601")){
			throw new UserNotLoggedException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion que envia al servidor la orden de apagar el sensor de numero pasado por parametro.
	 * @param idSensor
	 * @return true si apaga el sensor, false si ya estaba apagado.
	 * @throws IOException
	 * @throws SensorNotExistsException
	 * @throws IncorrectCodeException
	 * @throws SensorOFFException 
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean OFF(int idSensor) throws IOException, SensorNotExistsException, IncorrectCodeException, SensorOFFException, UserNotLoggedException, UnexpectedErrorException {
		sm.escribir("OFF " + idSensor);
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("214")){
			return true;
		}else if(s.equals("427")){
			throw new SensorNotExistsException();
		}else if(s.equals("429")){
			throw new SensorOFFException();
		}else if(s.equals("601")){
			throw new UserNotLoggedException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion que envia al servidor la instruccion de encender el GPS.
	 * @return true si se activa el GPS, false si ya estaba activado.
	 * @throws IOException
	 * @throws IncorrectCodeException 
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean ONGPS() throws IOException, IncorrectCodeException, UserNotLoggedException, UnexpectedErrorException{
		sm.escribir("ONGPS");
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("215")){
			return true;
		}else if(s.equals("429")){
			return false;
		}else if(s.equals("601")){
			throw new UserNotLoggedException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion que envia al servidor la instruccion de apagar el GPS.
	 * @return true si se desactiva el GPS, false si ya estaba desactivado.
	 * @throws IOException
	 * @throws IncorrectCodeException 
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean OFFGPS() throws IOException, IncorrectCodeException, UserNotLoggedException, UnexpectedErrorException{
		sm.escribir("OFFGPS");
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("216")){
			return true;
		}else if(s.equals("430")){
			return false;
		}else if(s.equals("601")){
			throw new UserNotLoggedException();
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion para obtener el valor actual del sensor de numero pasado por parametro.. 
	 * @param idSensor
	 * @return String con el valor actual del sensor.
	 * @throws UnknownSensorException
	 * @throws NullParamIDSensorException
	 * @throws SensorOFFException
	 * @throws IOException
	 * @throws IncorrectCodeException
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public String getValAct(int idSensor) throws UnknownSensorException, NullParamIDSensorException, SensorOFFException, IOException, IncorrectCodeException, UserNotLoggedException, UnexpectedErrorException{
		sm.escribir("GET_VALACT " + idSensor);
		String sInicial = sm.leer();
		StringTokenizer st = new StringTokenizer(sInicial, " ");
		String code = st.nextToken();
		if(code.equals("124")){
			return st.nextToken() + st.nextToken();
		}else if(code.equals("424")){
			throw new UnknownSensorException();
		}else if(code.equals("425")){
			throw new NullParamIDSensorException();
		}else if(code.equals("426")){
			throw new SensorOFFException();
		}else if(code.equals("601")){
			throw new UserNotLoggedException();
		}else if(code.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Funcion para obtener la localizacion GPS del tren.
	 * @return String con la posicion
	 * @throws IOException
	 * @throws IncorrectCodeException
	 * @throws UserNotLoggedException 
	 * @throws UnexpectedErrorException 
	 */
	public String getLoc() throws IOException, IncorrectCodeException, UserNotLoggedException, UnexpectedErrorException{
		sm.escribir("GET_LOC");
		String sInicial = sm.leer().trim();
		StringTokenizer st = new StringTokenizer(sInicial, " ");
		String code = st.nextToken();
		if(code.equals("124")){
			st.nextToken(); //Este sera el "OK".
			return st.nextToken(); //Esta es la localizacion GPS.
		}else if(code.equals("601")){
			throw new UserNotLoggedException();
		}else if(code.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Envia al servidor el comando actualizar.
	 * @return true si se ha actualizado correctamente, false si no.
	 * @throws IOException
	 * @throws IncorrectCodeException
	 * @throws ActTransferException 
	 * @throws ActLoginException 
	 * @throws UserNotLoggedException 
	 * @throws ActConnectException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean actualizar() throws IOException, IncorrectCodeException, ActTransferException, ActLoginException, UserNotLoggedException, ActConnectException, UnexpectedErrorException{
		sm.escribir("ACTUALIZAR");
		String sInicial = sm.leer().trim();
		StringTokenizer st = new StringTokenizer(sInicial, " ");
		String code = st.nextToken();
		if(code.equals("216")){
			return true;
		}else if(code.equals("430")){
			return false;
		}else if(code.equals("601")){
			throw new UserNotLoggedException();
		}else if(code.equals("603")){
			throw new ActLoginException();
		}else if(code.equals("604")){
			throw new ActTransferException();
		}else if(code.equals("605")){
			throw new ActConnectException();
		}else if(code.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}

	/**
	 * Cierra la conexion con el servidor
	 * @return true si se ha cerrado correctamente, false si no.
	 * @throws IOException
	 * @throws IncorrectCodeException 
	 * @throws UnexpectedErrorException 
	 */
	public boolean cerrar() throws IOException, IncorrectCodeException, UnexpectedErrorException{
		sm.escribir("SALIR");
		StringTokenizer st = new StringTokenizer(sm.leer().trim(), " ");
		String s = st.nextToken();
		if(s.equals("218")){
			return true;
		}else if(s.equals("666")){
			throw new UnexpectedErrorException();
		}else{
			throw new IncorrectCodeException();
		}
	}
}
